package com.lin.springbootinit.controller;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.*;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jd.platform.hotkey.client.callback.JdHotKeyStore;
import com.lin.maker.generator.main.MainTemplate;
import com.lin.maker.generator.main.ZipGenerator;
import com.lin.maker.meta.Meta;
import com.lin.maker.meta.MetaValidtor;
import com.lin.springbootinit.annotation.AuthCheck;
import com.lin.springbootinit.common.BaseResponse;
import com.lin.springbootinit.common.DeleteRequest;
import com.lin.springbootinit.common.ErrorCode;
import com.lin.springbootinit.common.ResultUtils;
import com.lin.springbootinit.constant.UserConstant;
import com.lin.springbootinit.exception.BusinessException;
import com.lin.springbootinit.exception.ThrowUtils;
import com.lin.springbootinit.manager.CacheManager;
import com.lin.springbootinit.manager.CosManager;
import com.lin.springbootinit.model.dto.generator.*;
import com.lin.springbootinit.model.entity.Generator;
import com.lin.springbootinit.model.entity.User;
import com.lin.springbootinit.model.vo.GeneratorVO;
import com.lin.springbootinit.service.GeneratorService;
import com.lin.springbootinit.service.UserService;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.utils.IOUtils;
import com.yupi.maker.generator.main.GenerateTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 代码生成器接口
 *
 * @author <a href="https://github.com/liyupi">程序员林</a>
 * @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
 */
@RestController
@RequestMapping("/generator")
@Slf4j
public class GeneratorController {

    @Resource
    private GeneratorService generatorService;

    @Resource
    private UserService userService;

    @Resource
    private CosManager cosManager;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CacheManager cacheManager;

    // region 增删改查

    /**
     * 创建代码生成器
     *
     * @param generatorAddRequest
     * @param request
     * @return
     */
    @PostMapping("/add")
    public BaseResponse<Long> addGenerator(@RequestBody GeneratorAddRequest generatorAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(generatorAddRequest == null, ErrorCode.PARAMS_ERROR);
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorAddRequest, generator);
        List<String> tags = generatorAddRequest.getTags();
        Meta.FileConfigDTO fileConfig = generatorAddRequest.getFileConfig();
        Meta.ModelConfigDTO modelConfig = generatorAddRequest.getModelConfig();
        if(CollUtil.isNotEmpty(tags)){
            generator.setTags(JSONUtil.toJsonStr(tags));
        }
        if(fileConfig != null){
            generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        }
        if(modelConfig != null){
            generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));
        }
        // 数据校验
        generatorService.validGenerator(generator, true);
        User loginUser = userService.getLoginUser(request);
        generator.setUserId(loginUser.getId());
        generator.setStatus(0);
        // 写入数据库
        boolean result = generatorService.save(generator);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回新写入的数据 id
        long newGeneratorId = generator.getId();
        return ResultUtils.success(newGeneratorId);
    }

    /**
     * 删除代码生成器
     *
     * @param deleteRequest
     * @param request
     * @return
     */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteGenerator(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getLoginUser(request);
        long id = deleteRequest.getId();
        // 判断是否存在
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可删除
        if (!oldGenerator.getUserId().equals(user.getId()) && !userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 操作数据库
        boolean result = generatorService.removeById(id);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 更新代码生成器（仅管理员可用）
     *
     * @param generatorUpdateRequest
     * @return
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateGenerator(@RequestBody GeneratorUpdateRequest generatorUpdateRequest) {
        if (generatorUpdateRequest == null || generatorUpdateRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorUpdateRequest, generator);
        List<String> tags = generatorUpdateRequest.getTags();
        Meta.FileConfigDTO fileConfig = generatorUpdateRequest.getFileConfig();
        Meta.ModelConfigDTO modelConfig = generatorUpdateRequest.getModelConfig();
        if(CollUtil.isNotEmpty(tags)){
            generator.setTags(JSONUtil.toJsonStr(tags));
        }
        if(fileConfig != null){
            generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        }
        if(modelConfig != null){
            generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));
        }
        // 数据校验
        generatorService.validGenerator(generator, false);
        // 判断是否存在
        long id = generatorUpdateRequest.getId();
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 操作数据库
        boolean result = generatorService.updateById(generator);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /**
     * 根据 id 获取代码生成器（封装类）
     *
     * @param id
     * @return
     */
    @GetMapping("/get/vo")
    public BaseResponse<GeneratorVO> getGeneratorVOById(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Generator generator = generatorService.getById(id);
        ThrowUtils.throwIf(generator == null, ErrorCode.NOT_FOUND_ERROR);
        // 获取封装类
        return ResultUtils.success(generatorService.getGeneratorVO(generator, request));
    }

    /**
     * 根据 id 获取代码生成器（封装类）（hotkey优化）
     *
     * @param id
     * @return
     */
    @GetMapping("/get/vo/hotkey")
    public BaseResponse<GeneratorVO> getGeneratorVOByIdHotKey(long id, HttpServletRequest request) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);

        // 先判断热键中是否有缓存
        String cacheKey = "generatorVO_" + id;  // 可以自定义缓存的key
        if (JdHotKeyStore.isHotKey(cacheKey)) {
            Object cachedGeneratorVO = JdHotKeyStore.get(cacheKey);
            if(cachedGeneratorVO==null){
                throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"缓存为空");
            }else {
                log.info("命中缓存");
                return ResultUtils.success((GeneratorVO)cachedGeneratorVO);
            }
        }
        // 查询数据库
        Generator generator = generatorService.getById(id);
        ThrowUtils.throwIf(generator == null, ErrorCode.NOT_FOUND_ERROR);

        // 获取封装类
        GeneratorVO generatorVO = generatorService.getGeneratorVO(generator, request);
        // 只有当数据被判定为热键之后，才将结果存入缓存
        // 这里的判断逻辑是：如果它是热键（比如满足访问频率等条件），就存入热键缓存
        if (JdHotKeyStore.isHotKey(cacheKey)) {
            JdHotKeyStore.smartSet(cacheKey, generatorVO);  // 将查询结果存入热键缓存
            cacheManager.putCache(cacheKey, generatorVO);  // 将查询结果存入缓存
        }
        return ResultUtils.success(generatorVO);
    }


    /**
     * 分页获取代码生成器列表（仅管理员可用）
     *
     * @param generatorQueryRequest
     * @return
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<Generator>> listGeneratorByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 查询数据库
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        return ResultUtils.success(generatorPage);
    }

    /**
     * 分页获取代码生成器列表（封装类）
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                               HttpServletRequest request) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        Page<GeneratorVO> generatorVOPage = generatorService.getGeneratorVOPage(generatorPage, request);
        // 获取封装类
        return ResultUtils.success(generatorVOPage);
    }
    /**
     * 分页获取代码生成器列表（封装类）(优化后的）
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/list/page/vo/fast")
    public BaseResponse<Page<GeneratorVO>> listGeneratorVOByPageFast(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                 HttpServletRequest request) {
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        String key = getCachePage(generatorQueryRequest);
        Object cacheValue = cacheManager.getCache(key);

        //先查询缓存
        if(!ObjectUtil.isEmpty(cacheValue)){
            //TypeReference 的主要用途是 在反序列化过程中保留和传递泛型信息，尤其是当目标类型是 带泛型的容器类（如 List<T>、Map<K, V>、Page<T> 等）
            // 简单点说，就是JSONUtil.toBean第二个参数需要一个类Class<T> 这样一个参数，但是当你的类是List<T>这样的，就会遇到泛型擦除，我们就需要用一个这个new TypeReference
//            Page<GeneratorVO> generatorVOPage = JSONUtil.toBean(cacheValue, new TypeReference<Page<GeneratorVO>>() {
//            }, false);

            return ResultUtils.success((Page<GeneratorVO>)cacheValue);
        }
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);

        QueryWrapper<Generator> queryWrapper = generatorService.getQueryWrapper(generatorQueryRequest);
        queryWrapper.select("id","name","description","tags","picture","status","userId","createTime","updateTime");
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size), queryWrapper);
        Page<GeneratorVO> generatorVOPage = generatorService.getGeneratorVOPage(generatorPage, request);

        //写入缓存
//        cacheManager.putCache(key,JSONUtil.toJsonStr(generatorVOPage));
        cacheManager.putCache(key,generatorVOPage);
        return ResultUtils.success(generatorVOPage);
    }

    /**
     * 分页获取当前登录用户创建的代码生成器列表
     *
     * @param generatorQueryRequest
     * @param request
     * @return
     */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<GeneratorVO>> listMyGeneratorVOByPage(@RequestBody GeneratorQueryRequest generatorQueryRequest,
                                                                 HttpServletRequest request) {
        ThrowUtils.throwIf(generatorQueryRequest == null, ErrorCode.PARAMS_ERROR);
        // 补充查询条件，只查询当前登录用户的数据
        User loginUser = userService.getLoginUser(request);
        generatorQueryRequest.setUserId(loginUser.getId());
        long current = generatorQueryRequest.getCurrent();
        long size = generatorQueryRequest.getPageSize();
        // 限制爬虫
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        Page<Generator> generatorPage = generatorService.page(new Page<>(current, size),
                generatorService.getQueryWrapper(generatorQueryRequest));
        // 获取封装类
        return ResultUtils.success(generatorService.getGeneratorVOPage(generatorPage, request));
    }

    /**
     * 编辑代码生成器（给用户使用）
     *
     * @param generatorEditRequest
     * @param request
     * @return
     */
    @PostMapping("/edit")
    public BaseResponse<Boolean> editGenerator(@RequestBody GeneratorEditRequest generatorEditRequest, HttpServletRequest request) {
        if (generatorEditRequest == null || generatorEditRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Generator generator = new Generator();
        BeanUtils.copyProperties(generatorEditRequest, generator);
        List<String> tags = generatorEditRequest.getTags();
        Meta.FileConfigDTO fileConfig = generatorEditRequest.getFileConfig();
        Meta.ModelConfigDTO modelConfig = generatorEditRequest.getModelConfig();
        if(CollUtil.isNotEmpty(tags)){
            generator.setTags(JSONUtil.toJsonStr(tags));
        }
        if(fileConfig != null){
            generator.setFileConfig(JSONUtil.toJsonStr(fileConfig));
        }
        if(modelConfig != null){
            generator.setModelConfig(JSONUtil.toJsonStr(modelConfig));
        }
        // 数据校验
        generatorService.validGenerator(generator, false);
        User loginUser = userService.getLoginUser(request);
        // 判断是否存在
        long id = generatorEditRequest.getId();
        Generator oldGenerator = generatorService.getById(id);
        ThrowUtils.throwIf(oldGenerator == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人或管理员可编辑
        if (!oldGenerator.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        // 操作数据库
        boolean result = generatorService.updateById(generator);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    // endregion

    /**
     * 根据id下载对应资源
     * @param id
     * @param request
     * @param response
     */
    @GetMapping("/download")
    public void downloadGeneratorById(Long id, HttpServletRequest request, HttpServletResponse response) throws IOException {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        Generator generator = generatorService.getById(id);
        String filepath = generator.getDistPath();
        COSObject object = cosManager.getObject(filepath);
        ThrowUtils.throwIf(generator == null, ErrorCode.NOT_FOUND_ERROR);
        User loginUser = userService.getLoginUser(request);
        // 追踪事件
        log.info("用户 {} 下载了 {}", loginUser, filepath);

        //优先从缓存中读取
        String cacheFilePath = getCacheFilePath(id, filepath);
        if(FileUtil.exist(cacheFilePath)){
            // 设置响应头
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
            Files.copy(Paths.get(cacheFilePath), response.getOutputStream());
            return;
        }


        InputStream cosObjectInput = null;
        try {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();

            COSObject cosObject = cosManager.getObject(filepath);
            cosObjectInput = cosObject.getObjectContent();
            // 非流式
            byte[] bytes = null;
            bytes = IOUtils.toByteArray(cosObjectInput);

            stopWatch.stop();
            System.out.println("下载总耗时:"+stopWatch.getTotalTimeMillis());
            // 设置响应头
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
            // 写入响应
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();
            //  将下载的文件流式返回给前端
//             设置响应头
//            response.setContentType("application/octet-stream;charset=UTF-8");
//            response.setHeader("Content-Disposition", "attachment; filename=" + filepath);
//            try (OutputStream out = response.getOutputStream()){
//                byte[] buffer = new byte[4096];
//                int bytesRead;
//                while ((bytesRead = cosObjectInput.read(buffer)) != -1) {
//                    out.write(buffer, 0, bytesRead);
//                }
//            }
        } catch (Exception e){
            e.printStackTrace();
        }finally {
            // 用完流之后一定要调用 close()
            cosObjectInput.close();
        }
    }

    /**
     * 使用生成器
     * @param generatorUseRequest
     * @param request
     * @param response
     */
    @PostMapping("/use")
    public void useGenerator(@RequestBody GeneratorUseRequest generatorUseRequest, HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 获取用户输入的请求参数
        Long id = generatorUseRequest.getId();
        Generator generator = generatorService.getById(id);
        ThrowUtils.throwIf(generator == null, ErrorCode.NOT_FOUND_ERROR);
        String distPath = generator.getDistPath();
        ThrowUtils.throwIf(distPath == null, ErrorCode.NOT_FOUND_ERROR);
        Map<String, Object> dataModel = generatorUseRequest.getDataModel();

        // 校验用户登录
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        // 记录用户行为
        log.info("用户 {} 下载了 id 为 {}得生成器", loginUser.getUserAccount(), id);

        // 从COS中下载生成器的压缩包并存储到我们当前服务器得自定义工作空间中
        // 自己定义一个工作空间
        String projectPath = System.getProperty("user.dir");
        String tempPath = String.format("%s/.temp/use/%s", projectPath, id);
        String zipPath = tempPath + File.separator + "dist.zip";
        if(!FileUtil.exist(zipPath)){
            FileUtil.touch(zipPath);
        }
        try {
            cosManager.download(distPath, zipPath);
        } catch (InterruptedException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"下载生成器失败");
        }

        // 解压压缩包 得到脚本文件
        File unzipDistDir = ZipUtil.unzip(zipPath);
        // 将用户输入的参数写道 json文件中
        String dataModelFilePath = tempPath + "/dataModel.json";
        String dataModelJsonStr = JSONUtil.toJsonStr(dataModel);
        FileUtil.writeUtf8String(dataModelJsonStr, dataModelFilePath);
        // 执行脚本文件
        // 先找到generate.bat文件 方式就是通过遍历
        File scriptFile  = FileUtil.loopFiles(unzipDistDir, 2, null).stream()
                .filter(file -> file.isFile() && "generate.bat".equals(file.getName()))
                .findFirst().orElseThrow(RuntimeException::new);
        // 获取脚本文件的父目录并且 将 \\ 转义为 /
        String scriptFileAbsoulutePath = scriptFile.getAbsolutePath().replace("\\", "/");
        File scriptParentFile = scriptFile.getParentFile();
        /**
         * 这里报了无权限的问题：
         * 我一开始往String[] commands 里面加的文件路径是：
         * D:/Code-Generate/code-generate/lin-code-generate-maker/lin-generator-web-backend/.temp/use/18/dist/acm-template-pro-generator
         * 这是一个文件夹：
         * 正确的应该是：
         * D:/Code-Generate/code-generate/lin-code-generate-maker/lin-generator-web-backend/.temp/use/18/dist/acm-template-pro-generator/generate.bat
         * 这个文件夹下的bat可执行文件
         */
        String[] commands = new String[] {scriptFileAbsoulutePath, "json-generate", "--file=" + dataModelFilePath};
        ProcessBuilder processBuilder = new ProcessBuilder(commands);
        processBuilder.directory(scriptParentFile);
        Process process = null;
        try {
            process = processBuilder.start();
            InputStream inputStream = process.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
            int exitCode = process.waitFor();
            System.out.println("命令执行退出码"+exitCode);
            bufferedReader.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"执行脚本时出错");
        }

        // 压缩得到得生成结果，返回给前端
        // 生成代码文件的位置
        String generateCodeDir = scriptParentFile + File.separator + "generated";
        // 在自己创建临时空间中找一个地方存放生成代码文件的压缩包
        String zipFilePath = tempPath + File.separator + "result.zip";
        File resultZipFile = ZipUtil.zip(generateCodeDir, zipFilePath);
        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + resultZipFile.getName());
        // 写入响应
        Files.copy(resultZipFile.toPath(), response.getOutputStream());

        // 已经返回给前端了，可以进行异步清理服务器文件
        CompletableFuture.runAsync(() -> {
            FileUtil.del(tempPath);
        });
    }


    /**
     * 制作生成器（用户自定义模板）
     * @param generatorMakeRequest
     * @param request
     * @param response
     */
    @PostMapping("/make")
    public void makeGenerator(@RequestBody GeneratorMakeRequest generatorMakeRequest,
                              HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 判断用户自定义上传的模板是否存在
        String zipPath = generatorMakeRequest.getZipPath();
        if(StrUtil.isBlank(zipPath)){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"用户自定义模板不存在");
        }
        // 需要用户登录
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);

        // 定义独立的工作空间,来存放下载的模板文件（这个和我们之前的maker项目很像）
        String property = System.getProperty("user.dir");
//        String id = IdUtil.getSnowflakeNextId() + RandomUtil.randomNumbers(6);
        String id = RandomUtil.randomNumbers(6);
        String tempDirPath = String.format("%s/.temp/make/%s", property, id);

        // 从文件存储中下载压缩包
        String localZipPath = tempDirPath + File.separator + "project.zip";
        // 如果不存在就新建
        if(!FileUtil.exist(localZipPath)){
            FileUtil.touch(localZipPath);
        }
        try {
            cosManager.download(zipPath,localZipPath);
        } catch (InterruptedException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"下载用户自定义模板失败");
        }

        // 解压到工作空间
        File unzipDistDir = ZipUtil.unzip(localZipPath);

        // 构造meta信息
        Meta meta = generatorMakeRequest.getMeta();
        String sourceRootPath  = unzipDistDir.getAbsolutePath();
        meta.getFileConfig().setSourceRootPath(sourceRootPath);
        MetaValidtor.doValid(meta);
        String outputPath = String.format("%s/generated/%s",tempDirPath,meta.getName());

        // 调用制作工具
        MainTemplate mainTemplate = new ZipGenerator();
        try {
            mainTemplate.doGenerator(meta,outputPath);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"制作生成器失败");
        }
        String suffix = "-dist.zip";
        String zipFileName = meta.getName() + suffix;
        String zipFilePath = outputPath + suffix;

        // 设置响应头
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + meta.getName());
        // 写入响应
        Files.copy(Paths.get(zipFilePath), response.getOutputStream());

        // 已经返回给前端了，可以进行异步清理服务器文件
        CompletableFuture.runAsync(() -> {
            FileUtil.del(tempDirPath);
        });
    }



    /**
     * 根据id下载对应资源
     * @param generatorCacheRequest
     * @param request
     * @param response
     */
    @PostMapping("/cache")
    @AuthCheck(mustRole = "admin")
    public void cacheGenerator(@RequestBody GeneratorCacheRequest generatorCacheRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 校验参数
        ThrowUtils.throwIf(generatorCacheRequest == null, ErrorCode.PARAMS_ERROR);

        //获取生成器
        Long id = generatorCacheRequest.getId();
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        Generator generator = generatorService.getById(id);
        ThrowUtils.throwIf(generator == null, ErrorCode.NOT_FOUND_ERROR);
        String filepath = generator.getDistPath();
        ThrowUtils.throwIf(filepath == null, ErrorCode.NOT_FOUND_ERROR,"产物包不存在");

        // 获取缓存空间
        String cacheFilePath = getCacheFilePath(id, filepath);
        if(!FileUtil.exist(cacheFilePath)){
            FileUtil.touch(cacheFilePath);
        }

        //下载生成器
        try {
            cosManager.download(filepath,cacheFilePath);
        } catch (InterruptedException e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"缓存产物包失败");
        }
    }

    /**
     * 获取缓存文件路径
     * @param id
     * @param distPath
     * @return
     */
    public String getCacheFilePath(Long id,String  distPath){
        String property = System.getProperty("user.dir");
        String tempDirPath = String.format("%s/.temp/cache/%s", property, id);
        String zipFilePath = tempDirPath + File.separator + distPath;
        return zipFilePath;
    }


    /**
     * 获取主页缓存的key
     * @param generatorQueryRequest
     * @return
     */
    public static String getCachePage(GeneratorQueryRequest generatorQueryRequest){
        String jsonStr = JSONUtil.toJsonStr(generatorQueryRequest);
        String base64 = Base64Encoder.encode(jsonStr);
        String key = "generator:page:" + base64;
        return key;
    }
}
